package team.archai.nexus.flow.endpoint;


import org.activiti.engine.ProcessEngine;
import org.activiti.engine.RepositoryService;
import org.activiti.engine.repository.Deployment;
import org.activiti.engine.repository.Model;
import org.activiti.engine.repository.ModelQuery;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.util.Base64Utils;
import org.springframework.web.bind.annotation.*;
import team.archai.nexus.boot.web.response.ResponseModel;
import team.archai.nexus.boot.web.response.ResultUtil;
import team.archai.nexus.flow.dto.ModelDTO;
import team.archai.nexus.flow.service.IProcessBasicsData;
import team.archai.nexus.flow.service.IProcessEngines;
import team.archai.nexus.flow.service.IProcessManage;
import team.archai.nexus.flow.vo.*;

import java.nio.charset.StandardCharsets;
import java.util.List;

/**
 * @program: flow
 * @description: 流程设计器端点
 * @author: lishibin
 * @create: 2023-08-02 18:03
 **/
@RestController
@RequestMapping("flow-api")
@ConditionalOnProperty(value = "archai-nexus.flow-designer.enabled", havingValue = "true")
public class FlowDesignerEndpoint {


    @Autowired
    private IProcessEngines processEngines;

    @Autowired
    private IProcessManage manage;

    @Autowired
    private IProcessBasicsData processBasicsData;

    private static ProcessEngine processEngine;

    public FlowDesignerEndpoint() {
    }

    private ProcessEngine getProcessEngine() {
        if (null == processEngine) {
            processEngine = this.processEngines.getDefault();
        }

        return processEngine;
    }

    private RepositoryService getRepositoryService() {
        return this.getProcessEngine().getRepositoryService();
    }

    private Model fillModel(ModelDTO dto) {
        RepositoryService repositoryService = this.getRepositoryService();
        Model model = repositoryService.newModel();
        String bpmnXml = new String(Base64Utils.decodeFromString(dto.getBpmnXmlBase64()));
        dto.setBpmnXml(bpmnXml);
        dto.fillModel(model);
        return model;
    }

    /**
     * 新增或修改
     */
    @PostMapping("/model")
    public ResponseModel<Model> submit(@RequestBody ModelDTO dto) {
        RepositoryService repositoryService = this.getRepositoryService();
        Model model = this.fillModel(dto);
        String processKey = dto.getKey();
        String modelId = dto.getId();
        ModelQuery modelQuery = repositoryService.createModelQuery();
        modelQuery.modelKey(processKey);
        long count = modelQuery.count();
        if (null != modelId && !"".equals(modelId)) {
            if (count > 1L) {
                return ResultUtil.error("Duplicate key " + processKey);
            }

            model = repositoryService.getModel(modelId);
        } else if (count > 0L) {
            return ResultUtil.error("Duplicate key " + processKey);
        }

        dto.fillModel(model);
        repositoryService.saveModel(model);
        repositoryService.addModelEditorSource(model.getId(), dto.getBpmnXml().getBytes(StandardCharsets.UTF_8));
        return ResultUtil.success(model);
    }

    @PostMapping({"/model/save-and-deploy"})
    public ResponseModel<Model> saveAndDeployModel(@RequestBody ModelDTO dto) {
        RepositoryService repositoryService = this.getRepositoryService();
        Model model = this.fillModel(dto);
        String processKey = dto.getKey();
        String modelId = dto.getId();
        ModelQuery modelQuery = repositoryService.createModelQuery();
        modelQuery.modelKey(processKey);
        long count = modelQuery.count();
        if (null != modelId && !"".equals(modelId)) {
            if (count > 1L) {
                return ResultUtil.error("Duplicate key " + processKey);
            }

            model = repositoryService.getModel(modelId);
        } else if (count > 0L) {
            return ResultUtil.error("Duplicate key " + processKey);
        }

        dto.fillModel(model);
        Deployment deploy = repositoryService.createDeployment().category(model.getCategory()).name(model.getName()).key(model.getKey()).category(model.getCategory()).enableDuplicateFiltering().addString(model.getKey() + ".bpmn20.xml", dto.getBpmnXml()).disableBpmnValidation().disableSchemaValidation().deploy();
        model.setDeploymentId(deploy.getId());
        repositoryService.saveModel(model);
        repositoryService.addModelEditorSource(model.getId(), dto.getBpmnXml().getBytes(StandardCharsets.UTF_8));
        FlowModelVO flowModelVO = new FlowModelVO();
        flowModelVO.setId(model.getId());
        flowModelVO.setThirdModelId(dto.getThirdModelId());
        this.manage.onDeployModel(flowModelVO);
        return ResultUtil.success(model);
    }


    @GetMapping({"/models/{modelId}"})
    public ResponseModel<Object> modelInfo(@PathVariable String modelId) {
        FlowModelVO flowModelVO = this.manage.getModelById(modelId);
        RepositoryService repositoryService = this.getRepositoryService();
        if (null != flowModelVO) {
            modelId = flowModelVO.getId();
            if (null == modelId || "".equals(modelId)) {
                return ResultUtil.success(flowModelVO);
            }
        }

        Model model = repositoryService.getModel(modelId);
        String bpmnXml = new String(repositoryService.getModelEditorSource(modelId));
        model.setMetaInfo(bpmnXml);
        return ResultUtil.success(model);
    }

    @GetMapping({"/assignee-type"})
    public ResponseModel<List<AssigneeTypeVO>> assigneeType() {
        return ResultUtil.success(processBasicsData.assigneeTypes());
    }

    @GetMapping({"/flow-listeners/{listenerType}"})
    public ResponseModel<List<FlowListenerVO>> taskListeners(@PathVariable Integer listenerType) {
        if (listenerType.equals(1)) {
            return ResultUtil.success(processBasicsData.taskListeners());
        }
        return ResultUtil.success(manage.execListeners());
    }

    @GetMapping({"/assignees/{assigneeType}"})
    public ResponseModel<List<AssigneeVO>> assignees(@PathVariable Integer assigneeType) {
        return ResultUtil.success(manage.assignees(assigneeType));
    }

    @GetMapping({"/condition-category"})
    public ResponseModel<List<ConditionCategoryVO>> conditionCategories() {
        return ResultUtil.success(processBasicsData.conditionCategories());
    }

    @GetMapping({"/flow-categories"})
    public ResponseModel<List<FlowBaseVO>> categories() {
        return ResultUtil.success(processBasicsData.categories());
    }

    @GetMapping({"/assignees-conditions/{conditionCategory}"})
    public ResponseModel<List<ConditionVO>> conditions(@PathVariable Integer conditionCategory) {
        return ResultUtil.success(processBasicsData.conditions(conditionCategory));
    }

    @GetMapping({"/flow-nodes"})
    public ResponseModel<List<FlowNodeVO>> flowNodes() {
        return ResultUtil.success(processBasicsData.flowNodes());
    }

}
